/*
 *    Copyright 2009-2023 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       https://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;

/**
 * @author Clinton Begin
 */
public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
      BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      // 获取Configuration对象
      Configuration configuration = ms.getConfiguration();
      // 创建RoutingStatementHandler，用来处理Statement
      // RoutingStatementHandler类中初始化delegate类（SimpleStatementHandler、PreparedStatementHandler）
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
          boundSql);
      // 子流程1：设置参数
      stmt = prepareStatement(handler, ms.getStatementLog());
      /**
       * BaseStatementHandler：基础语句处理器（抽象类），它基本把语句处理器接口的核心部分都实现了，包括配置绑定、执行器绑定、映射器绑定、参数处理器构建、结果集处理器构建、语句超时设置、语句关闭等，并另外定义了新的方法 instantiateStatement 供不同子类实现以便获取不同类型的语句连接，子类可以普通执行 SQL 语句，也可以做预编译执行，还可以执行存储过程等。
       *
       * SimpleStatementHandler：普通语句处理器，继承 BaseStatementHandler 抽象类，对应 java.sql.Statement 对象的处理，处理普通的不带动态参数运行的 SQL，即执行简单拼接的字符串语句，同时由于 Statement 的特性，SimpleStatementHandler 每次执行都需要编译 SQL （注意：我们知道 SQL 的执行是需要编译和解析的）。
       *
       * PreparedStatementHandler：预编译语句处理器，继承 BaseStatementHandler 抽象类，对应 java.sql.PrepareStatement 对象的处理，相比上面的普通语句处理器，它支持可变参数 SQL 执行，由于 PrepareStatement 的特性，它会进行预编译，在缓存中一旦发现有预编译的命令，会直接解析执行，所以减少了再次编译环节，能够有效提高系统性能，并预防 SQL 注入攻击（所以是系统默认也是我们推荐的语句处理器）。
       *
       * CallableStatementHandler：存储过程处理器，继承 BaseStatementHandler 抽象类，对应 java.sql.CallableStatement 对象的处理，很明了，它是用来调用存储过程的，增加了存储过程的函数调用以及输出/输入参数的处理支持。
       *
       * RoutingStatementHandler：路由语句处理器，直接实现了 StatementHandler 接口，作用如其名称，确确实实只是起到了路由功能，并把上面介绍到的三个语句处理器实例作为自身的委托对象而已，所以执行器在构建语句处理器时，都是直接 new 了 RoutingStatementHandler 实例。
       * 原文链接：https://blog.csdn.net/qq_52751442/article/details/133046391
       *
       * query() 执行 PreparedStatementHandler
       * 子流程2：执行SQL语句（已经设置过参数），并且映射结果集
       */
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
      throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    Cursor<E> cursor = handler.queryCursor(stmt);
    stmt.closeOnCompletion();
    return cursor;
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) {
    return Collections.emptyList();
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取连接
    Connection connection = getConnection(statementLog);
    // 创建Statement（PreparedStatement、Statement、CallableStatement）
    stmt = handler.prepare(connection, transaction.getTimeout());
    // SQL参数设置   --> 揭秘如何设置的参数？
    handler.parameterize(stmt);
    return stmt;
  }

}
